package com.blue.gateway.jwt;

import com.alibaba.fastjson.JSON;
import com.blue.common.exception.BaseException;
import com.blue.gateway.bean.constants.JwtConfig;
import com.blue.user.model.entity.SysLoginUserInfo;
import com.google.common.collect.Maps;
import io.jsonwebtoken.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.Map;

/**
 * jwt token工具类
 *
 * @author liulei
 * @version 1.0
 */
@Slf4j
@AllArgsConstructor
public class JwtTokenUtil {

    @Getter
    private RsaKeyConfig keyConfig;

    private static JwtTokenUtil jwtUtil;

    @PostConstruct
    public void init() {
        jwtUtil = this;
    }

    /**
     * 生成token,根据userId和默认过期时间(私钥加密)
     */
    @SneakyThrows
    public static String generateToken(SysLoginUserInfo userInfo) {
        Map<String, Object> userInfoMap = Maps.newHashMap();
        Date expireDate = new Date(System.currentTimeMillis() + JwtConfig.expire * 1000);
        Class<?> aClass = userInfo.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            userInfoMap.put(field.getName(), field.get(userInfo));
        }
        return Jwts.builder()
                // 加密数据主体标识
                .setSubject(userInfo.getAccount())
                // 数字加密主体
                .setClaims(userInfoMap)
                // 数字加密发行时间
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(expireDate)
                // 数字签名算法
                .signWith(SignatureAlgorithm.RS256, RsaKeyUtil.getPrivateKey(jwtUtil.getKeyConfig().getPrivateKey()))
                // 根据规则进行加密
                .compact();
    }

    /**
     * 获取jwt的claims部分(公钥解密token)
     */
    private static Claims getClaimFromToken(String token) throws ExpiredJwtException {
        if (StringUtils.isEmpty(token)) {
            throw new IllegalArgumentException("token参数为空！");
        }
        Jws<Claims> claimsJws;
        try {
            claimsJws = Jwts.parser()
                    .setSigningKey(RsaKeyUtil.getPublicKey(jwtUtil.getKeyConfig().getPublicKey()))
                    .parseClaimsJws(token);
        } catch (ExpiredJwtException e) {
            // parseClaimsJws(token)方法对过期有自动的判断
            throw new BaseException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "登录已过期");
        } catch (Exception e) {
            log.error("token解析异常", e);
            throw new BaseException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Token解析异常");
        }
        return claimsJws.getBody();
    }

    /**
     * 获取jwt的payload部分
     */
    public static SysLoginUserInfo getJwtPayLoad(String token) {
        Claims info = getClaimFromToken(token);
        Object parse = JSON.parse(JSON.toJSONString(info));
        return JSON.toJavaObject((JSON) parse, SysLoginUserInfo.class);
    }

    /**
     * 解析token是否正确(true-正确, false-错误)
     */
    @SneakyThrows
    public static Boolean checkTokenFormat(String token) {
        try {
            Jwts.parser().setSigningKey(RsaKeyUtil.getPublicKey(jwtUtil.getKeyConfig().getPublicKey())).parseClaimsJws(token).getBody();
            return true;
        } catch (JwtException e) {
            return false;
        }
    }

    /**
     * 验证token是否失效
     */
    public static Boolean tokenIsExpired(String token) {
        try {
            final Date expiration = getClaimFromToken(token).getExpiration();
            return expiration.before(new Date());
        } catch (ExpiredJwtException expiredJwtException) {
            return true;
        }
    }
}